package com.terminal;

import android.graphics.Color;

class CharPanel {
	int iCursorX = 0;
	int iCursorY = 0;
	int iSizeX = 1;
	int iSizeY = 1;
	CharPoint[][] Matrix;
	boolean isDirty = true;
	boolean[] isDirtyLine = null;
	int iRegionTop = 0;
	int iRegionBottom = iSizeY - 1;
	boolean isG1 = false;
	int ts = TerminalState.REGULAR;
	Params prm = new Params();
	int clrBgCurrent = CharPoint.defBg;
	int clrFgCurrent = CharPoint.defFg;
	int clrBgDef = CharPoint.defBg;
	int clrFgDef = CharPoint.defFg;

	CharPanel(int width, int height) {
		iSizeX = width;
		iSizeY = height;
		Matrix = new CharPoint[iSizeX][iSizeY];
		isDirtyLine = new boolean[iSizeY];
		for (int x = 0; x < iSizeX; x++)
			for (int y = 0; y < iSizeY; y++)
				Matrix[x][y] = new CharPoint(' ');
		for (int y = 0; y < iSizeY; y++)
			isDirtyLine[y] = true;
		this.iRegionTop = 0;
		this.iRegionBottom = iSizeY - 1;
		this.iCursorX = 0;
		this.iCursorY = 0;
	}


	public void AddBuffer(char[] ar) {

		for (int i = 0; i < ar.length; i++) {
			try {
				AddChar(ar[i]);
			} catch (Exception ex) {
			}
		}

	}

	void AddChar(char ch) {
		switch (ts) {
		case TerminalState.REGULAR:
			if (ch == 13) {
				SetCurPos(0, iCursorY);
				// curParent.History.NewLine();
			} else if (ch == 10)
				LF();
			else if (ch == 0x00 || ch == 0x07) {
			} else if (ch == 9) {
				int x = (iCursorX / 8) * 8 + 8;
				if (x >= iSizeX)
					x = iSizeX - 1;
				iCursorX = x;
				isDirtyLine[iCursorY] = true;
			} else if (ch == 0x0f) {
				isG1 = false;
			} else if (ch == 0x0e) {
				isG1 = true;
			} else if (ch == 12 // FF
					|| ch == 11) // VT
				LF();
			else if (ch == 27) // ESC
				ts = TerminalState.ESC;
			else if (ch == 0x9B) // CSI
				ts = TerminalState.CSI;
			else if (ch == 0x08) // Delete char
				ProcessBS();
			else {
				AddChar(ch, clrFgCurrent, clrBgCurrent);
				// History.AddChar(ch);
			}
			break;
		case TerminalState.ESC:
			ProcessESC(ch);
			break;
		case TerminalState.CSI:
			if (ch == '?')
				ts = TerminalState.MODE;
			else
				ProcessMode(ch);
			break;
		case TerminalState.G0:
			CommandG0(ch);
			ts = TerminalState.REGULAR;
			break;
		case TerminalState.G1:
			CommandG1(ch);
			ts = TerminalState.REGULAR;
			break;
		case TerminalState.MODE:
			ProcessMode(ch);
			break;

		}
	}

	private void ProcessMode(char ch) {
		if (ch >= '0' && ch <= '9') {
			prm.setCurrentParam(prm.getCurrentParam() * 10 + (ch - '0'));
			return;
		}
		if (ch == ';') {
			prm.nextParam();
			return;
		}
		switch (ts) {
		case TerminalState.MODE:
			CommandMode(ch);
			break;
		case TerminalState.CSI:
			CommandCSI(ch);
			break;
		case TerminalState.G0:
		    CommandG0(ch);
		    break;
		case TerminalState.G1:
			CommandG1(ch);
			break;

		}
		prm.Clear();
		ts = TerminalState.REGULAR;
	}

	private void CommandMode(char ch) {
		// ch = ch;
	}

	private void CommandG0(char ch) {
		switch (ch) {
		case '0':
			isG1 = true;
			break;
		case 'B':
			isG1 = false;
			break;
		default:
			isG1 = false;
			break;

		}
	}

	private void CommandG1(char ch) {
		isG1 = true;
	}

	private void CommandCSI(char ch) {
		switch (ch) {
		case 'A': // Cursor up no scroll
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0) {
				iCursorY--;
			} else {
				if (prm.getParam(0) == 0)
					iCursorY--;
				else
					iCursorY -= prm.getParam(0);
			}
			if (iCursorY < iRegionTop)
				iCursorY = iRegionTop;
			isDirtyLine[iCursorY] = true;
			break;
		case 'B': // Cursor down no scroll
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0) {
				iCursorY++;
			} else {
				if (prm.getParam(0) == 0)
					iCursorY++;
				else
					iCursorY += prm.getParam(0);
			}
			if (iCursorY >= iRegionBottom)
				iCursorY = iRegionBottom;
			isDirtyLine[iCursorY] = true;
			break;
		case 'C': // Cursor right
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0)
				iCursorX++;
			else if (prm.getParam(0) == 0)
				iCursorX++;
			else
				iCursorX += prm.getParam(0);
			if (iCursorX >= iSizeX)
				iCursorX = iSizeX - 1;
			break;
		case 'D': // Cursor left
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0)
				iCursorX--;
			else if (prm.getParam(0) == 0)
				iCursorX--;
			else
				iCursorX -= prm.getParam(0);
			if (iCursorX < 0)
				iCursorX = 0;
			break;

		case 'E': // Cursor next line
			break;
		case 'F': // Cursor prev line
			break;
		case 'H': // Set cursor pos
		case 'f':
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0) {
				iCursorX = 0;
				iCursorY = 0;
				break;
			}
			if (prm.getCount() >= 1)
				iCursorY = prm.getParam(0) - 1;
			if (prm.getCount() >= 2)
				iCursorX = prm.getParam(1) - 1;

			isDirtyLine[iCursorY] = true;
			break;
		case 'r': // Set region
			if (prm.getCount() == 0) {
				iRegionTop = 0;
				iRegionBottom = iSizeY - 1;
			} else {
				iRegionTop = prm.getParam(0) - 1;
				iRegionBottom = prm.getParam(1) - 1;
			}
			break;
		case 'I': // Go to tabstop
			break;

		case 'm':
			if (prm.getCount() == 0) {
				clrBgCurrent = clrBgDef;
				clrFgCurrent = clrFgDef;
			} else {
				if (prm.getCount() > 0)
					SetColor(prm.getParam(0));
				if (prm.getCount() > 1)
					SetColor(prm.getParam(1));
				if (prm.getCount() > 2)
					SetColor(prm.getParam(2));
			}
			break;
		case 'c': // Terminal TYPE report
			// curParent.SendBytes(new byte[] { 0x1b, 0x5b, 0x3f, 0x31, 0x3b,
			// 0x30, 0x63 });
			break;

		case 'n': // Corsor pos report
			break;
		case 'J': // Clear screen
			isDirty = true;
			if (prm.getCount() == 0 || prm.getParam(0) == 0) // to end
			{
				for (int i = iCursorX; i < iSizeX; i++) {
					ClearChar(i, iCursorY);
				}
				for (int y = iCursorY + 1; y < iSizeY; y++)
					for (int x = 0; x < iSizeX; x++)
						ClearChar(x, y);
				break;
			}
			if (prm.getParam(0) == 1) // from bottom
			{
				for (int y = 0; y < iCursorY; y++)
					for (int x = 0; x < iSizeX; x++)
						ClearChar(x, y);
				for (int x = 0; x <= iCursorX; x++) {
					ClearChar(x, iCursorY);
				}
				break;
			}
			if (prm.getParam(0) == 2) // all
			{
				for (int y = 0; y < iSizeY; y++)
					for (int x = 0; x < iSizeX; x++)
						ClearChar(x, y);
			}
			break;
		case 'K': // Clear line
			isDirtyLine[iCursorY] = true;
			if (prm.getCount() == 0 || prm.getParam(0) == 0) // to end
			{
				for (int x = iCursorX; x < iSizeX; x++) {
					ClearChar(x, iCursorY);
				}
				break;
			}
			if (prm.getParam(0) == 1) // from begin
			{
				for (int x = 0; x <= iCursorX; x++)
					ClearChar(x, iCursorY);
				break;
			}
			if (prm.getParam(0) == 2) // all
			{
				for (int x = 0; x < iSizeX; x++)
					ClearChar(x, iCursorY);
			}
			break;
		case 'L': // Insert line above
			break;
		case 'M': // Delete line
			break;

		default:
			break;

		}

	}

	private void SetColor(int prm) {
		int tmp;
		switch (prm) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:

			clrFgCurrent = clrFgDef;
			clrBgCurrent = clrBgDef;
			break;
		case 7:
			tmp = clrFgCurrent;
			clrFgCurrent = clrBgCurrent;
			clrBgCurrent = tmp;
			break;
		case 30:
			clrFgCurrent = Color.BLACK;
			break;
		case 31:
			clrFgCurrent = Color.RED;
			break;
		case 32:
			clrFgCurrent = Color.GREEN;
			break;
		case 33:
			clrFgCurrent = Color.YELLOW;
			break;
		case 34:
			clrFgCurrent = Color.BLUE;
			break;
		case 35:
			clrFgCurrent = Color.rgb(238, 130, 238);// Color.Violet;
			break;
		case 36:
			clrFgCurrent = Color.CYAN;
			break;
		case 37:
			clrFgCurrent = Color.rgb(211, 211, 211);// Light Gray
			break;
		case 40:
			clrBgCurrent = Color.BLACK;
			break;
		case 41:
			clrBgCurrent = Color.RED;
			break;
		case 42:
			clrBgCurrent = Color.GREEN;
			break;
		case 43:
			clrBgCurrent = Color.YELLOW;
			break;
		case 44:
			clrBgCurrent = Color.BLUE;
			break;
		case 45:
			clrBgCurrent = Color.rgb(238, 130, 238);// Color.Violet;
			break;
		case 46:
			clrBgCurrent = Color.CYAN;
			break;
		case 47:
			clrBgCurrent = Color.rgb(211, 211, 211);// Light Gray
			break;
		case 90:
			clrFgCurrent = Color.rgb(169, 169, 169);// Dark Gray
			break;
		case 91:
			clrFgCurrent = Color.rgb(255, 192, 203);// Color.Pink;
			break;
		case 92:
			clrFgCurrent = Color.rgb(144, 238, 144);// Color.LightGreen;
			break;
		case 93:
			clrFgCurrent = Color.rgb(255, 255, 224);// Color.LightYellow;
			break;
		case 94:
			clrFgCurrent = Color.rgb(173, 216, 230);// Color.LightBlue;
			break;
		case 95:
			clrFgCurrent = Color.MAGENTA;
			break;
		case 96:
			clrFgCurrent = Color.rgb(224, 255, 255);// Color.LightCyan;
			break;
		case 97:
			clrFgCurrent = Color.WHITE;
			break;
		case 100:
			clrBgCurrent = Color.rgb(169, 169, 169);// Dark Gray
			break;
		case 101:
			clrBgCurrent = Color.rgb(255, 192, 203);// Color.Pink;
			break;
		case 102:
			clrBgCurrent = Color.rgb(144, 238, 144);// Color.LightGreen;
			break;
		case 103:
			clrBgCurrent = Color.rgb(255, 255, 224);// Color.LightYellow;
			break;
		case 104:
			clrBgCurrent = Color.rgb(173, 216, 230);// Color.LightBlue;
			break;
		case 105:
			clrBgCurrent = Color.MAGENTA;
			break;
		case 106:
			clrBgCurrent = Color.rgb(224, 255, 255);// Color.LightCyan;
			break;
		case 107:
			clrBgCurrent = Color.WHITE;
			break;
		default:
			clrFgCurrent = clrFgDef;
			clrBgCurrent = clrBgDef;
			break;
		}
	}

	private void ProcessESC(char ch) {
		switch (ch) {
		case '[': // CSI
			ts = TerminalState.CSI;
			break;
		case '>': // Сброс терминала
			// charPanel.Reset(clrFgDef, clrBgDef);
			ts = TerminalState.REGULAR;
			break;
		case '7': // Сохранить курсор
			ts = TerminalState.REGULAR;
			break;
		case '8': // Восстановить курсор
			ts = TerminalState.REGULAR;
			break;
		case 'D': // Индекс Курсор вниз со скроллигом
			isDirty = true;
			iCursorY++;
			// if (charPanel.iCursorX >= CharPanel.iSizeX)
			// charPanel.iCursorX = CharPanel.iSizeX - 1;
			SetCurPos(iCursorX, iCursorY);
			ts = TerminalState.REGULAR;
			break;
		case 'E': // Следующая строка
			ts = TerminalState.REGULAR;
			break;
		case 'M': // Реверс перевода строки (курсор вверх без скроллинга ???)
			isDirty = true;
			iCursorY--;
			SetCurPos(iCursorX, iCursorY);

			ts = TerminalState.REGULAR;
			break;
		case 'H': // Установка табуляции ИЛИ домой
			isDirtyLine[iCursorY] = true;
			iCursorX = 0;
			iCursorY = 0;
			isDirtyLine[0] = true;
			ts = TerminalState.REGULAR;
			break;
		case 'I': // Реверсивный перевод строки
			ts = TerminalState.REGULAR;
			break;
		case 'c': // Сброс в начальное состояние
			isDirty = true;
			for (int y = 0; y < iSizeY; y++)
				for (int x = 0; x < iSizeY; x++)
					ClearChar(x, y);
			clrBgCurrent = clrBgDef;
			clrFgCurrent = clrFgDef;
			iCursorX = 0;
			iCursorY = 0;
			iRegionBottom = iSizeY - 1;
			iRegionTop = 0;

			ts = TerminalState.REGULAR;
			break;
		case '(': // Переход в режим G0
			ts = TerminalState.G0;
			break;
		case ')': // Переход в режим G1
			ts = TerminalState.G1;
			break;
		default: // Непонятный ESC
			ts = TerminalState.REGULAR;
			break;
		}
	}

	void AddChar(char chr, int fg, int bg) {
		char c = chr;
		if (iCursorX >= iSizeX) {
			isDirty = true;
			LF();
			iCursorX = 0;
		}
		if (isG1) // ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼
		{
			switch (c) {
			case 'q':
				c = '─';
				break;
			case 'x':
				c = '│';
				break;
			case 'l':
				c = '┌';
				break;
			case 'k':
				c = '┐';
				break;
			case 't':
				c = '├';
				break;
			case 'u':
				c = '┤';
				break;
			case 'm':
				c = '└';
				break;
			case 'j':
				c = '┘';
				break;
			case 'a':
				c = '░';
				break;
			case 'w':
				c = '┬';
				break;
			case 'v':
				c = '┴';
				break;

			}
		}

		try {
			Matrix[iCursorX][iCursorY].chr = c;
			Matrix[iCursorX][iCursorY].clrBg = bg;
			Matrix[iCursorX][iCursorY].clrFg = fg;

			isDirtyLine[iCursorY] = true;
			iCursorX++;
		} catch (Exception ex) {
		}
	}

	void Reset(int fg, int bg) {
		for (int y = iCursorY; y < iRegionBottom; y++)
			for (int x = iCursorX; x < iSizeX; x++) {
				Matrix[x][y].chr = ' ';
				Matrix[x][y].clrBg = bg;
				Matrix[x][y].clrFg = fg;
			}
		isDirty = true;

	}

	void Clear() {
		for (int y = 0; y < iSizeY; y++)
			for (int x = 0; x < iSizeX; x++) {
				Matrix[x][y].Clear();
			}
		this.iRegionTop = 0;
		this.iRegionBottom = iSizeY - 1;
		this.iCursorX = 0;
		this.iCursorY = 0;
		isDirty = true;

	}

	void ProcessBS() {
		if (iCursorX > 0) {
			isDirtyLine[iCursorY] = true;
			iCursorX--;
			// Matrix[iCursorX, iCursorY].chr = ' ';
		}
	}

	void ClearDirty() {
		isDirty = false;
		for (int y = 0; y < iSizeY; y++)
			isDirtyLine[y] = false;
	}

	void LF() {
		isDirty = true;
		iCursorY++;
		SetCurPos(iCursorX, iCursorY);
	}

	void SetCurPos(int x, int y) {
		if (y < iRegionTop) {
			LineDown(iRegionTop - y);
			iCursorY = iRegionTop;
			iCursorX = x;
			return;
		}
		if (y > iRegionBottom) {
			LineUp(y - iRegionBottom);
			iCursorY = iRegionBottom;
			iCursorX = x;
			return;
		}
		iCursorX = x;
		iCursorY = y;
	}

	void LineUp(int count) {
		for (int i = 0; i < count; i++)
			LineUp();
	}

	void LineDown(int count) {
		for (int i = 0; i < count; i++)
			LineDown();
	}

	private void LineUp() {
		isDirty = true;
		for (int y = iRegionTop; y < iRegionBottom; y++)
			for (int x = 0; x < iSizeX; x++) {
				Matrix[x][y].Copy(Matrix[x][y + 1]);
			}
		for (int x = 0; x < iSizeX; x++) {
			ClearChar(x, iRegionBottom);
		}
	}

	void ClearChar(int x, int y) {
		Matrix[x][y].Clear();
	}

	private void LineDown() {
		isDirty = true;
		for (int y = iRegionBottom - 1; y >= iRegionTop; y--)
			for (int x = 0; x < iSizeX; x++) {
				Matrix[x][y + 1].Copy(Matrix[x][y]);
			}
		for (int x = 0; x < iSizeX; x++) {
			ClearChar(x, iRegionTop);
		}
	}
}

class CharPoint {
	final static int defFg = Color.rgb(192, 192, 192);
	final static int defBg = Color.rgb(0, 0, 0);

	char chr = ' ';
	int clrFg = CharPoint.defFg;
	int clrBg = CharPoint.defBg;

	CharPoint(char chr) {
		this.chr = chr;
	}

	CharPoint(char chr, int fg, int bg) {
		this.chr = chr;
		this.clrBg = bg;
		this.clrFg = fg;
	}

	void Copy(CharPoint cp) {
		chr = cp.chr;
		clrFg = cp.clrFg;
		clrBg = cp.clrBg;
	}

	void Clear() {
		this.chr = ' ';
		this.clrFg = CharPoint.defFg;
		this.clrBg = CharPoint.defBg;
	}

}

class TerminalState {
	static final int REGULAR = 0;
	static final int ESC = 1;
	static final int CSI = 2; // Esc-[
	static final int G0 = 3; // Esc-(
	static final int G1 = 4; // Esc-)
	static final int MODE = 5; // Esc-[
}
